#include "rdx_interpret.hpp"
#include "rdx_pccm_api.hpp"
#include "rdx_pccm_env.hpp"


//================================================================================
int RDX_DECL_API rdxResumeThread(rdxSOperationContext *ctx, rdxIObjectManager *objm, rdxWeakRTRef(rdxCRuntimeThread) thread)
{
	int status = rdxRS_Active;

	// Cycle through code until the status turns into an exit code
	while(status > 0)
	{
		rdxWeakRTRef(rdxCMethod) method;	// FIXME: Bad constructor
		method = thread->frame.method;

		if(method.IsNull())
			return rdxRS_Exited;

		rdxPCCMCallback pccmCallback = method->m_native.pccmCallback;
		if(pccmCallback)
			status = rdxCPrecompiledCodeModule::ResumeThread(ctx, objm, thread, pccmCallback);
		else
		{
			rdxDebugBreak(rdxBREAKCAUSE_Unimplemented);	status = 0;
			//status = rdxResumeThreadInterpreted(ctx, objm, thread);
		}

		if(status == rdxRS_AbandonFrame)
		{
			// Code signalled to abandon the frame, determine the type of frame being entered
			rdxSRuntimeStackFrame *rsf = thread->frame.bp;
			if(rsf->method.IsNotNull())
			{
				// Standard frame
				thread->frame = *rsf;
				status = rdxRS_Active;
			}
			else
			{
				// Root-level call frame
				rdxRuntimeStackFrameRoot *rframe = static_cast<rdxRuntimeStackFrameReentrant *>(rsf);
				thread->insertionPoint = rframe->insertionPoint;
				if(rframe->aboveNative)
				{
					// Reentrant frame
					rdxRuntimeStackFrameReentrant *ref = static_cast<rdxRuntimeStackFrameReentrant *>(rframe);
					thread->activeNativeMethod = ref->nativeMethod;
					thread->frame = ref->subNativeFrame;
					return rdxRS_Exited;
				}
				else
				{
					// Absolute root frame
					thread->activeNativeMethod = rdxWeakRTRef(rdxCMethod)::Null();
					thread->Reset();
					return rdxRS_Exited;
				}
			}
		}
	}

	return status;
}

//====================================================================================
rdxCInterpreterCodeProvider::rdxCInterpreterCodeProvider(const rdxSAllocator &alloc)
{
	m_alloc = alloc;
}

void rdxCInterpreterCodeProvider::CreateExecutable(rdxIObjectManager *objm, rdxSOperationContext *ctx, rdxWeakHdl(rdxCMethod) m) const
{
	if(const rdxSSerializationTag *serTag = m->ObjectInfo()->SerializationTag())
	{
		rdxSObjectGUID objectGUID = serTag->gstSymbol;
		rdxUInt64 codedDomain = 0;
		rdxUInt64 codedObject = 0;
		for(rdxLargeUInt i=0;i<rdxSDomainGUID::GUID_SIZE;i++)
			codedDomain = ((codedDomain << 8) | (objectGUID.m_domain.m_bytes[i]));
		for(rdxLargeUInt i=0;i<rdxSObjectGUID::GUID_SIZE;i++)
			codedObject = ((codedObject << 8) | (objectGUID.m_bytes[i]));

		const rdxINativeTypeHost *nth = objm->GetTypeHost();
		rdxLargeUInt nPlugins = nth->NumPlugins();
		for(rdxLargeUInt i=0;i<nPlugins;i++)
		{
			const rdxSPCCMDomainIndex *pccm;
			const rdxSPluginExport *plugin;
			nth->GetPlugin(i, &pccm, &plugin);
			while(pccm->objects != RDX_CNULL)
			{
				if(pccm->codedDomainGUID == codedDomain)
				{
					rdxLargeUInt nObjects = pccm->objCount;
					// TODO: Symbols are probably evenly distributed, so a simple initial guess will probably be good
					for(rdxLargeUInt obji=0;obji<nObjects;obji++)
					{
						if(pccm->objects[obji].codedObjectGUID == codedObject)
						{
							m->m_native.pccmCallback = pccm->objects[obji].callback;
							return;
						}
					}
				}
				pccm++;
			}
		}
	}
	// TODO: This must be fatal until interpreter is ready
	//rdxDebugBreak(rdxBREAKCAUSE_Unimplemented);
}

void rdxCInterpreterCodeProvider::ReleaseExecutable(rdxIObjectManager *objm, rdxWeakHdl(rdxCMethod) m) const
{
	rdxDebugBreak(rdxBREAKCAUSE_Unimplemented);
}

void rdxCInterpreterCodeProvider::InitializeSymbolDictionary(rdxSOperationContext *ctx, rdxIObjectManager *objm) const
{
}

int rdxCInterpreterCodeProvider::RunMethod(rdxSOperationContext *ctx, rdxIObjectManager *objm, rdxWeakHdl(rdxCMethod) m, rdxWeakHdl(rdxCRuntimeThread) thread, rdxURuntimeStackValue *prv) const
{
	rdxWeakRTRef(rdxCMethod) viaMethod;	// FIXME: Bad constructor
	viaMethod = thread->activeNativeMethod;
	int status;

	if(m->m_native.isNativeCall)
	{
		thread->activeNativeMethod = m.ToWeakRTRef();
		status = m->m_native.nativeCall(ctx, objm, m, thread, prv);
	}
	else
	{
		const void *stackBottom = thread->stackBytes + thread->stackCapacity;
		bool methodEntered;
		{
			methodEntered = rdxEnterMethodRoot(objm, m.ToWeakRTRef(), &thread->precallFrame, thread->precallFrame.bp, thread->stackBytes, prv, &thread->frame, thread->insertionPoint, (thread->insertionPoint != stackBottom), viaMethod.ToWeakRTRef(), &thread->frame);
		}
		if(!methodEntered)
		{
			rdxDebugBreak(rdxBREAKCAUSE_Unimplemented);
			/*
			rdxRef<rdxCException> stackOverflowException = objm->GetBuiltIns()->providerDictionary.ToHandle()[rdxX_StackOverflowException].StaticCast<rdxCException>();

			{
				rdxCDataView<rdxHandle<rdxCRuntimeThread> > threadView(thread);
				threadView->ex = stackOverflowException.ToHandle();
			}
			*/
			return rdxRS_Exception;
		}
		
		thread->activeNativeMethod = rdxWeakRTRef(rdxCMethod)::Null();
		status = rdxResumeThread(ctx, objm, thread);
	}
	return status;
}

rdxIPToCurrentInstructionCallback rdxCInterpreterCodeProvider::GetIPToCurrentInstructionCallback() const
{
	return RDX_CNULL;
}

rdxInstrNumToIPCallback rdxCInterpreterCodeProvider::GetInstrNumToIPCallback() const
{
	return RDX_CNULL;
}

rdxResumeThreadCallback rdxCInterpreterCodeProvider::GetResumeThreadCallback(rdxIObjectManager *objm) const
{
	return RDX_CNULL;
}

void rdxCInterpreterCodeProvider::Shutdown()
{
	m_alloc.Free(this);
}
